package com.gbei.center.aop;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDateTime;
import java.util.*;

import javax.servlet.http.HttpServletRequest;

import com.alibaba.fastjson.JSONObject;
import com.gbei.center.aop.operlog.OperLog;
import com.gbei.center.aop.operlog.OperLogDO;
import com.gbei.center.aop.operlog.OperModuleGroup;
import com.gbei.center.aop.operlog.OperUserType;
import com.gbei.center.model.OpenApplication;
import com.gbei.center.model.OpenApplicationRequest;
import com.gbei.center.model.OpenUserOauth;
import com.gbei.center.oauth.mapper.*;
import com.gbei.center.oauth.model.AuthAccessToken;
import com.gbei.center.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.expression.EvaluationContext;
import org.springframework.stereotype.Component;

import com.gbei.center.exception.GbeiException;
import com.gbei.center.message.ResponseMessage;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * @Discription ${在此输入一句话描述此文件的作用}
 * @Author yang jun
 * @Date 2019/6/4 9:14
 */

@Aspect
@Component
public class ControllerAspect{

    @Autowired
    private OpenApplicationMapper openApplicationMapper;

    @Autowired
    private OpenIpWhiteMapper openIpWhiteMapper;

    @Autowired
    private AuthAccessTokenMapper authAccessTokenMapper;

    @Autowired
    private OpenApplicationRequestMapper openApplicationRequestMapper;

    @Autowired
    private OpenUserOauthMapper openUserOauthMapper;


    @Value("${open.ip.white.enable:false}")
    private Boolean openIpWhiteEnable;

    @SuppressWarnings("unused")
    private static Logger LOGGER = LoggerFactory.getLogger(ControllerAspect.class);


    @Pointcut("execution(public * com.gbei.center.oauth.controller.aopController..*(..))")
    public void executeService() {
    }

    @Before("executeService()")
    public void doBeforeAdvice(JoinPoint joinpoint) throws Exception {
        HttpServletRequest request = HttpServletRequestUtil.getRequest();

        Map<String, String> reqParams = reqParamsProcess(request.getParameterMap());

        //获取接口路径 （如 "/platform/user/getInfo"）
        String uri = request.getRequestURI();

        LOGGER.info("@@@@@@@@@ 接口URI:{}, 参数reqParams:{}",uri, reqParams);


        String accessToken = reqParams.get("token");

        //token校验
        if(!StringUtils.isNoneBlank(accessToken)){
            throw new GbeiException(ResponseMessage.FAILURE, "token不能为空");
        }
        //查询数据库中的Access Token
        AuthAccessToken authAccessToken = authAccessTokenMapper.selectByAccessToken(accessToken);

        if (authAccessToken == null){
            throw new GbeiException(ResponseMessage.TOKEN_INVALID, "");
        }

        Long savedExpiresAt = authAccessToken.getExpiresIn();
        //过期日期
        LocalDateTime expiresDateTime = DateUtils.ofEpochSecond(savedExpiresAt, null);
        //当前日期
        LocalDateTime nowDateTime = DateUtils.now();

        //如果Access Token已经失效，则返回错误提示
        if(!expiresDateTime.isAfter(nowDateTime))  {
            throw new GbeiException(ResponseMessage.TOKEN_EXPIRED, "");
        }

        String appId = authAccessToken.getClientId();

        // 时间戳校验，只允许十分钟内的请求，以北京时间为准
        String timestampStr = reqParams.get("timestamp");
        if (StringUtils.isBlank(timestampStr)) {
            throw new GbeiException(ResponseMessage.FAILURE, "时间戳参数不能为空");
        } else {
            long timestamp = Long.parseLong(timestampStr);
            //10分钟内有效
            long nowTime = Math.abs(System.currentTimeMillis());
            if (nowTime - timestamp > 600000) {
                throw new GbeiException(ResponseMessage.FAILURE, "时间戳参数过期");
            }
        }

        OpenApplication openApp =  openApplicationMapper.selectByClientId(appId);

        //判断请求Ip是否合法
        if(openIpWhiteEnable) {
            String realIp = IpUtil.getIpAddr(request);
            LOGGER.info("@@@@@@@@@@@@@@ Aspect-currentIp:{}", realIp);
            if(!"0:0:0:0:0:0:0:1".equals(realIp)) {
                List<String> ipList = openIpWhiteMapper.listIp(openApp.getOpenApplicationId());
                if(! IPWhiteListUtil.checkIpList(realIp, ipList)) {
                    throw new GbeiException(ResponseMessage.FAILURE, "非法IP,请联系管理员");
                }
            }
        }

        String appSecret = openApp.getAppSecret();

        //获取校验类型 读取参数的校验类型
       /* String signType = reqParams.get("signType");

        //根据接口判断签名方式是否正确
        if (uri.endsWith("orderCommon") || uri.contains("waterMachine")){
            if (!"RSA2".equalsIgnoreCase(signType)){
                throw new GbeiException(ResponseMessage.FAILURE, "接口的签名类型错误");
            }
        }else {
            if (!"MD5".equalsIgnoreCase(signType)){
                throw new GbeiException(ResponseMessage.FAILURE, "接口的签名类型错误");
            }
        }*/

        //校验接口签名类型 读取数据库中的签名类型
        String signType = openApp.getSignType();

        if (signType.equalsIgnoreCase("RSA2")){
            //获取公钥
            String publicKey = openApp.getSignPublicKey();
            if (null == publicKey) {
                throw new GbeiException(ResponseMessage.FAILURE, "PublicKey is Empty");
            }

            //接口数字签名校验
            if (!GbeiRsaSignature.signCheck(reqParams, publicKey, appSecret)) {
                throw new GbeiException(ResponseMessage.FAILURE, "无效的sign");
            }
        }

        if (signType.equalsIgnoreCase("MD5")){
            //md5校验
            if (!GbeiRsaSignature.checkMd5(reqParams,appSecret)) {
                throw new GbeiException(ResponseMessage.FAILURE, "无效的sign");
            }
        }
    }


/**
 * HttpServletRequest Map<String, String[]> 转换成 Map<String, String>
 *
 * @param reqParams
 * @return
 */
  private Map<String, String> reqParamsProcess(Map<String, String[]> reqParams) throws UnsupportedEncodingException {
    Map<String, String> map = new HashMap<>();
    Iterator<Map.Entry<String, String[]>> it = reqParams.entrySet().iterator();
    Map.Entry<String, String[]> entry;
    while (it.hasNext()) {
      entry = it.next();
      if (null != entry.getValue()) {
        map.put(entry.getKey(), entry.getValue()[0]);
      }
    }
    return map;
  }

    /**
     * 返回通知, 目标方法正常执行结束后执行以下代码  日志保存
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "executeService()")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) {
        try {
            HttpServletRequest request = HttpServletRequestUtil.getRequest();
            String realIp = IpUtil.getIpAddr(request);
            //参数
            Map<String, String> reqParams = reqParamsProcess(request.getParameterMap());
            String uri = HttpServletRequestUtil.getUri(request);
            String token = request.getParameter("token");
            //查询数据库中的Access Token
            AuthAccessToken authAccessToken = authAccessTokenMapper.selectByAccessToken(token);
            String appId = authAccessToken.getClientId();
            OpenApplication open = openApplicationMapper.selectByClientId(appId);

            //授权
            if (uri.contains("platform/user/getInfo")){

                OpenUserOauth oauth = new OpenUserOauth();
                oauth.setUserOauthId(IdUtil.getId());
                oauth.setAppId(appId);
                oauth.setOpenUserId(open.getOpenUserId());
                oauth.setOauthType(1);
                oauth.setRequestIp(realIp);
                oauth.setRequestJson(JSONObject.toJSONString(reqParams));
                oauth.setResponseJson(JSONObject.toJSONString(ret));
                oauth.setStatus(true);
                openUserOauthMapper.insertSelective(oauth);
            }else {

                OpenApplicationRequest openRequest = new OpenApplicationRequest();
                openRequest.setAppRequestId(IdUtil.getId());
                openRequest.setOpenUserId(open.getOpenUserId());
                openRequest.setAppId(appId);
                openRequest.setRequestIp(realIp);
                openRequest.setRequestApi(uri);
                openRequest.setRequestType(2);
                openRequest.setStatus(true);
                openRequest.setRequestJson(JSONObject.toJSONString(reqParams));
                openRequest.setResponseJson(JSONObject.toJSONString(ret));
                openApplicationRequestMapper.insertSelective(openRequest);
            }

        }
        catch (Exception e) {
            LOGGER.error(ExceptionUtils.getStackTrace(e));
        }
    }


    /**
     * 获取参数
     * @param request
     * @return
     */
    public static Map<String,String> showParams(HttpServletRequest request) {
        Map<String,String> map = new HashMap<>();
        Enumeration paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = (String) paramNames.nextElement();

            String[] paramValues = request.getParameterValues(paramName);
            if (paramValues.length >0) {
                String paramValue = paramValues[0];
                if (paramValue.length() != 0) {
                    map.put(paramName, paramValue);
                }
            }
        }

        Set<Map.Entry<String, String>> set = map.entrySet();
        for (Map.Entry entry : set) {
            LOGGER.info(entry.getKey() + ":" + entry.getValue());
        }
        return map;
    }

}
